#include <jni.h>

#include <gui/SurfaceComposerClient.h>
#include <utils/Log.h>
#include <gui/ISurfaceComposer.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

#include "caevent.h"
#include "calog.h"
#include "cathread.h"
#include "CGetPutBuf.h"
#include "capturesr.h"
#include "renderscr.h"
#include "turbojpeg.h"


#define SHOT_W 720
#define SHOT_H 1280



#define CTX (&gRenderCtx)
es_t gRenderCtx;

extern JavaVM *gJavaVM;

tjhandle gEnc;


tjhandle gDec;
extern CGetPutBuf* gGPBuf;

static int gdecw, gdech, gPixelByte;
static void* gRawImgBuf;

static unsigned char* gEncImgBuf;

//JNIEnv *gEnv, jobject gSurface;
extern jobject gMirrorSurface;

int attach_thread(void);
void detach_thread(void);


void render_rawimg(void* decbuf)
{
	JNIEnv *env;
	if(gJavaVM->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
		logprt("### Error: render : get env error,\n");
		exit(1);
		return;
	}

	//logprt("== render start......\n");
	ANativeWindow* window = ANativeWindow_fromSurface(env, gMirrorSurface);
	//logprt("== window = %p\n", window);
	ANativeWindow_Buffer buffer;
	if (ANativeWindow_lock(window, &buffer, NULL) == 0) {
		logprt("== win buf=%p, w=%d, h=%d, f=%d, stride=%d \n", buffer.bits, buffer.width, buffer.height, buffer.format, buffer.stride);
		char* ptr = (char*)buffer.bits;
		int linebytes = buffer.stride*gPixelByte;
		char* penc = (char*)decbuf;
		
		//for(int i=0;i<1184;i++) {
		for(int i=0;i<buffer.height;i++) {
		  memcpy(ptr, penc, buffer.width*gPixelByte);
		  ptr += linebytes;
		  penc += buffer.width * gPixelByte;
		}

	  ANativeWindow_unlockAndPost(window);
	  //logprt("== render end, \n");
	}
	else
		logprt("## lock window error,\n");
	ANativeWindow_release(window);
	//logprt("== close......native window\n");

}


void open_scr_decoder(int width, int height)
{

	gdecw = width;
	gdech = height;
	gPixelByte = 4;
	gRawImgBuf = malloc(gdecw * gdech * gPixelByte); // 4 is header
	if(gRawImgBuf == NULL)
	{
		logprt("### Error: raw img buf alloc fail\n");
		exit(1);
	}
	gDec = tjInitDecompress();

	
	
}

void close_scr_decoder(void)
{
	tjDestroy(gDec);
	if(gRawImgBuf)
		free(gRawImgBuf);
	gRawImgBuf = NULL;
}


int decode_scr_jpeg(void* jpegBuf, unsigned long jpegSize, void *dstBuf)
{
	int pitch = gdecw * gPixelByte;
	int ret;
	//unsigned int t1,t2;
	//t1 = msec();
	ret = tjDecompress(gDec, (unsigned char*)jpegBuf, jpegSize, (unsigned char * )dstBuf, gdecw, pitch, gdech, gPixelByte, 0);
	logprt("== dec, w=%d, h=%d\n", gdecw, gdech);
	//t2 = msec();
	//logprt("== dec tj time=%d\n", t2-t1);
	//logprt("== dec ret=%d, jpeg_size=%d\n", ret, jpegSize);
	return ret;
}


void open_scr_encoder(void)
{
	gEnc = tjInitCompress();
	gEncImgBuf = tjAlloc(PKT_BUF_SIZE);
	
}

void close_scr_encoder(void)
{
	tjFree(gEncImgBuf);
	tjDestroy(gEnc);
}


unsigned char* encode_scr_jpeg(void* rawimg, unsigned long *jpegSize, int width, int height)
{
	int ret;
	unsigned char* penc = gEncImgBuf+PKT_HDR_SIZE;
	*jpegSize = PKT_BUF_SIZE-512;
	ret = tjCompress2(gEnc, (unsigned char*)rawimg, width, width*PIXEL_BYTE, height, TJPF_RGBA, &penc, jpegSize, TJSAMP_420, 30, TJFLAG_NOREALLOC);	
	//logprt("== encode, ret=%d, size=%d, rawimg=%p\n", ret, *jpegSize, rawimg);
	*((unsigned int*)gEncImgBuf) = (*jpegSize+PKT_HDR_SIZE);
	if(ret!=0)
	{
		logprt("### Error: encoding error, ret=%d \n", ret);
	}
	return (ret==0) ? gEncImgBuf : NULL;
}

unsigned char* encode_raw_jpeg(void* rawimg, unsigned long *jpegSize, int width, int height)
{
	int ret;
	unsigned char* penc = gEncImgBuf;
	*jpegSize = PKT_BUF_SIZE-512;
	ret = tjCompress2(gEnc, (unsigned char*)rawimg, width, width*PIXEL_BYTE, height, TJPF_RGBA, &penc, jpegSize, TJSAMP_420, 30, TJFLAG_NOREALLOC);	
	if(ret!=0)
	{
		logprt("### Error: encoding error, ret=%d \n", ret);
	}
	return (ret==0) ? gEncImgBuf : NULL;
}


int RenderMsgProc(es_t* psys, uint16_t msgid, uint16_t p1, uint32_t p2)
{
#define TIMERID_RENDER 1
	int ret;
	if(msgid == EM_INIT)
	{
		logprt("== render MsgProc init\n");
		logprt("==    jpegbuf size=%d\n", tjBufSize(720, 1280, TJSAMP_420));
		logprt("==    jpegbuf w=%d, h=%d\n", rxinfo.imgh, rxinfo.imgh);
		attach_thread();
		//open_scr_decoder(720, 1280);
		//open_scr_decoder(480, 640);
		open_scr_decoder(rxinfo.imgw, rxinfo.imgh);

		
		{
			JNIEnv *env;
			if(gJavaVM->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
				logprt("### Error: render : get env error,\n");
				exit(1);
				return 0;
			}

			ANativeWindow* window = ANativeWindow_fromSurface(env, gMirrorSurface);
			if(rxinfo.imgw != 720) 
			{
				int err = ANativeWindow_setBuffersGeometry(window, rxinfo.imgw, rxinfo.imgh, 0);
				logprt("======== buffer geo, w=%d, h=%d, err=%d\n", rxinfo.imgw, rxinfo.imgh, err);
			}	
		}
	}
	else if(msgid == EM_CLOSE)
	{
		logprt("== render MsgProc close\n");
		es_kill_timer(psys, TIMERID_RENDER);
		close_scr_decoder();
		detach_thread();
	}
	else if(msgid == EM_TIMER)
	{
		unsigned char* pimg;
		logprt("== render timer.........\n");
		for(int cnt=0;cnt<1;cnt++)
		{
			pimg = (unsigned char*)gGPBuf->get_fill();
			if(!pimg) {
				logprt("== no enc img\n");
				break;
			}	
			ret = decode_scr_jpeg(pimg+PKT_HDR_SIZE, *((unsigned long*)pimg), gRawImgBuf);
			gGPBuf->put_empty(pimg);
			if(ret==0)
				render_rawimg(gRawImgBuf);
			else
				logprt("### Error: jpeg decoding error..\n");
		}
	}

	else if(msgid == UM_RENDER_START)
	{
		logprt("== start, render sampling......\n");
		es_set_timer(psys, TIMERID_RENDER, CAP_PERIOD, NULL);
	}
	else if(msgid == UM_RENDER_STOP)
	{
		logprt("== stop, render sampling......\n");
		es_kill_timer(psys, TIMERID_RENDER);
	}

	return 0;
}

void* render_thread(void* arg)
{
	es_open(CTX);
	es_register_msgproc(CTX, RenderMsgProc);
	es_main(CTX);
	es_close(CTX);
	return NULL;
}



int attach_thread(void)
{
	JNIEnv *env;
	int status = gJavaVM->GetEnv((void **)&env, JNI_VERSION_1_4);
	if(status < 0) {
		logprt("failed to get JNI environment, assuming native thread");
		status = gJavaVM->AttachCurrentThread(&env, NULL);
		if(status < 0) {
			logprt("### Error: failed to attach current thread");
		}
		return 0;
	}

	return -1;
}

void detach_thread(void)
{
	gJavaVM->DetachCurrentThread();

}



pthread_t gtid_render;
void start_render_thread(void)
{
	es_thread_start(&gtid_render, render_thread, NULL, CTX);
}

void stop_render_thread(void)
{
	es_exit(CTX);
	es_thread_stop(gtid_render);
}

void start_render_samp(void)
{
	es_postmsg(CTX, UM_RENDER_START, 0, 0);
}

void stop_render_samp(void)
{
	es_sendmsg(CTX, UM_RENDER_STOP, 0, 0);
}
